/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "file_uri.h"

#include <fcntl.h>

#include "bundle_info.h"
#include "bundle_mgr_proxy.h"
#include "log.h"
#include "ipc_skeleton.h"
#include "iservice_registry.h"
#include "status_receiver_host.h"
#include "system_ability_definition.h"

namespace OHOS {
namespace AppFileService {
namespace ModuleFileUri {

static bool IsUri(const string &uri)
{
    string::size_type posSymbol = uri.find(SCHEME_SYMBOLS);
    return posSymbol != string::npos;
}

static int32_t GetArgsFromUri(FileUri &fileUri,string &path, string &query)
{
    path = fileUri.GetPath();
    if (path.empty() || path.length() == LENGTH || path == PATH_EMPTY_SYMBOLS) {
        LOGE("Failed to GetArgsFromUri");
        return -errno;
    }
    if (path.substr(0, LENGTH) == PATH_SYMBOL) {
        path = path.substr(LENGTH);
    }

    query = fileUri.GetQuery();
    if (!query.empty()) {
        query = QUERY_SYMBOL + query;
    }
    return 0;
}

static bool IsAllDigits(string &idStr)
{
    for (size_t i = 0; i < idStr.size(); i++) {
        if (!isdigit(idStr[i])) {
            return false;
        }
    }
    return true;
}

bool FileUri::IsFileUri(const string &uri)
{
    if (IsUri(uri)) {
        FileUri fileUri(uri);
        string scheme = fileUri.GetScheme();
        string bundleName = fileUri.GetAuthority();
        return scheme == FILE_SCHEME && !bundleName.empty();
    }
    return false;
}

bool FileUri::IsMediaFileUri(const string &uri)
{
    if (IsFileUri(uri)) {
        FileUri fileUri(uri);
        string bundleName = fileUri.GetAuthority();
        return bundleName == MEDIA_BUNDLE_NAME;
    }
    return false;
}

bool FileUri::IsMediaUri(const string &uri)
{
    if (IsUri(uri)) {
        FileUri fileUri(uri);
        string scheme = fileUri.GetScheme();
        string path = fileUri.GetPath();
        std::size_t len = MEDIA.length();
        if (path.length() > len) {
            string media = path.substr(0, len);
            return scheme == DATA_SHARE_SCHEME && media == MEDIA;
        }
    }
    return false;
}

int32_t FileUri::GetIdFromUri(const string &uri, string &id)
{
    size_t pos = uri.rfind('/');
    if (pos == string::npos) {
        LOGE("Failed to GetIdFromUri, parameter is invalid! uri = %{private}s", uri.c_str());
        return -errno;
    }
    id = uri.substr(pos + LENGTH);
    if (!IsAllDigits(id)) {
        id = "";
        return -errno;
    }
    return 0;
}

int32_t FileUri::GetNetWorkIdFromUri(const string &uri, string &networkId)
{
    if (!IsFileUri(uri)) {
        LOGE("Failed to GetNetWorkIdFromUri, parameter is invalid! parameter = %{private}s", uri.c_str());
        return -errno;
    }
    networkId = "";
    string fragment = FileUri(uri).GetFragment();
    if (!fragment.empty()) {
        string::size_type pos = fragment.find(EQUAL_SIGN);
        if (pos == string::npos) {
            LOGE("Failed to GetNetWorkIdFromUri, fragment is not id!");
            return -errno;
        }
        networkId = fragment.substr(pos + 1);
    }
    return 0;
}

int32_t FileUri::TransToFileUri(const string &dataShareUri, string &uri)
{
    if (!IsUri(dataShareUri)) {
        LOGE("Failed to TransToFileUri, dataShareUri is invalid! dataShareUri = %{private}s", dataShareUri.c_str());
        return -errno;
    }
    FileUri fileUri(dataShareUri);
    string scheme = fileUri.GetScheme();
    if (scheme != DATA_SHARE_SCHEME) {
        LOGE("Failed to TransToFileUri, dataShareUri scheme is %{private}s!", scheme.c_str());
        return -errno;
    }
    string path, query, fragment;
    int32_t ret = GetArgsFromUri(fileUri, path, query);
    if (ret < 0) {
        LOGE("Failed to TransToFileUri, dataShareUri is %{private}s", dataShareUri.c_str());
        return ret;
    }
    string networkId = fileUri.GetAuthority();
    if (!networkId.empty()) {
        fragment = FRAGMENT_SYMBOLS + NETWORK_ID_TAG + EQUAL_SIGN + networkId;
    }
    uri = FILE_SCHEME + SCHEME_SYMBOLS + path + query + fragment;
    LOGI("Success to TransToFileUri, fileUri is %{private}s", uri.c_str());
    return 0;
}

int32_t FileUri::TransToDataShareUri(const string &uri, string &dataShareUri)
{
    if (!IsMediaFileUri(uri)) {
        LOGE("Failed to TransToDataShareUri, parameter is invalid! parameter = %{private}s", uri.c_str());
        return -errno;
    }
    FileUri fileUri(uri);
    string bundleName = fileUri.GetAuthority();
    string path, query;
    int32_t ret = GetArgsFromUri(fileUri, path, query);
    if (ret < 0) {
        LOGE("Failed to TransToDataShareUri, uri is %{private}s", uri.c_str());
        return ret;
    }
    string networkId;
    GetNetWorkIdFromUri(uri, networkId);
    dataShareUri = DATA_SHARE_SCHEME + SCHEME_SYMBOLS + networkId + PATH_SYMBOL + bundleName
     + PATH_SYMBOL + path + query;
    LOGI("Success to TransToDataShareUri, dataShareUri is %{private}s", dataShareUri.c_str());
    return 0;
}

static sptr<OHOS::AppExecFwk::BundleMgrProxy> GetBundleMgrProxy()
{
    sptr<ISystemAbilityManager> systemAbilityManager =
        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (!systemAbilityManager) {
        LOGE("fail to get system ability mgr.");
        return nullptr;
    }

    sptr<IRemoteObject> remoteObject = systemAbilityManager->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
    if (!remoteObject) {
        LOGE("fail to get bundle manager proxy.");
        return nullptr;
    }

    return iface_cast<OHOS::AppExecFwk::BundleMgrProxy>(remoteObject);
}

static string GetBundleNameSelf()
{
    int uid = -1;
    uid = IPCSkeleton::GetCallingUid();

    sptr<OHOS::AppExecFwk::BundleMgrProxy> bundleMgrProxy = GetBundleMgrProxy();
    if (!bundleMgrProxy) {
        LOGE("Bundle mgr proxy is null ptr.");
        return nullptr;
    }
    OHOS::AppExecFwk::BundleInfo bundleInfo;
    auto ret = bundleMgrProxy->GetBundleInfoForSelf(uid, bundleInfo);
    if (ret != ERR_OK) {
        LOGE("Failed to get bundleNameSelf.");
        return nullptr;
    }

    return bundleInfo.name;
}

static string GetPathFromFileUri(string &path, const string &bundleName, const unsigned int &mode)
{
    if (bundleName != GetBundleNameSelf()) {
        if ((mode & O_WRONLY) == O_WRONLY || (mode & O_RDWR) == O_RDWR) {
            path = PATH_SHARE + SHARE_RW_PATH + bundleName + path;
        } else {
            path = PATH_SHARE + SHARE_R_PATH + bundleName + path;
        }
    }
    return path;
}

int32_t FileUri::Open(const string &uri, uint32_t mode)
{
    int32_t fd = -1;

    if (!IsFileUri(uri)) {
        LOGE("Failed to Open, uri is %{private}s", uri.c_str());
        return fd;
    }

    FileUri fileUri(uri);
    string path = fileUri.GetPath();
    if (path.empty()) {
        LOGE("Failed to GetPath, path is invalid! uri is %{private}s", uri.c_str());
        return fd;
    }
    string bundleName = fileUri.GetAuthority();
    path = GetPathFromFileUri(path, bundleName, mode);
    LOGE("path is %{private}s", path.c_str());
    fd = open(path.c_str(), mode);
    return fd;
}

} // namespace ModuleFileUri
} // namespace AppFileService
} // namespace OHOS